#ifndef __MEMORYUTIL_H_
#define __MEMORYUTIL_H_

#include "base/nocopyable.h"
#include <stdio.h>
#include <memory.h>


#include <execinfo.h>   /*backtrace, backtrace_symbols*/
#include <string.h>     /*strlen*/


namespace tb
{
#define DUMP_PRINT printf
class MemoryUtil : public nocopyable
{
public:
    static void HexPrintBuffer(const void* pBuf, int nLen);
    static void HexPrintBuffer(const void* pBuf, int nLen, int addr);
    static void executeCMD(const char *cmd, char *result); 
    static void PrintStack();
    static void PrintStack(const char* pFileName);
};

void MemoryUtil::HexPrintBuffer(const void* pBuf, int nLen)
{
    if (NULL == pBuf || 0 == nLen)
    {
        return;
    }

    const int nBytePerLine = 16;

    unsigned char* p = (unsigned char*)pBuf;
    char szHex[3 * nBytePerLine + 1] = { 0 };
    //char* pszHex = new char[3 * nBytePerLine + 1];
    DUMP_PRINT("--------------- begin ------------\n");
    for (unsigned int i = 0; i < nLen; i++)
    {
        int idx = 3 * (i % nBytePerLine);
        if (0 == idx)
        {
            memset(szHex, 0, 3 * nBytePerLine + 1);
        }
#ifdef WIN32
        sprintf_s(&szHex[idx], 4, "%02x ", p[i]);
#else
        snprintf(&szHex[idx], 4, "%02x ", p[i]);
#endif
        // 打印一整行
        if (0 == ((i + 1) % nBytePerLine))
        {
            DUMP_PRINT("%s\n", szHex);
        }
    }
    
    if (0 != (nLen % nBytePerLine))
    {
        DUMP_PRINT("%s\n", szHex);
    }
    // delete [] pszHex;
    // pszHex = NULL;
    // 打印最后一行
    DUMP_PRINT("--------------- end ------------\n");
}

void MemoryUtil::HexPrintBuffer(const void* pBuf, int nLen, int addr)
{
    int i,j,k;
    char binstr[80];
 
    unsigned char* buf = (unsigned char*)pBuf;
    for (i = 0; i < nLen; i++) 
    {
        if (0 == (i % 16)) 
        {
            sprintf(binstr, "%08x ->", i + addr);
            sprintf(binstr, "%s %02x", binstr, buf[i]);
        } 
        else if (15 == (i % 16)) 
        {
            sprintf(binstr, "%s %02x", binstr, buf[i]);
            sprintf(binstr, "%s  ", binstr);
            for (j=i-15; j<=i; j++) 
            {
                sprintf(binstr,"%s%c",binstr,('!' < buf[j] && buf[j] <= '~')?buf[j]:'.');
            }
            DUMP_PRINT("%s\n",binstr);
        } 
        else 
        {
            sprintf(binstr, "%s %02x", binstr, buf[i]);
        }
    }
    if (0 != (i % 16)) 
    {
        k = 16- (i % 16);
        for (j=0; j<k; j++) 
        {
            sprintf(binstr, "%s   ", binstr);
        }
        sprintf(binstr, "%s  ", binstr);
        k = 16 - k;
        for (j = i - k; j < i; j++) 
        {
            sprintf(binstr, "%s%c", binstr, ('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
        }
        DUMP_PRINT("%s\n", binstr);
    }
}

void MemoryUtil::executeCMD(const char *cmd, char *result)   
{   
    char buf_ps[1024];   
    char ps[1024]={0};   
    FILE *ptr;   
    strcpy(ps, cmd);   
    if((ptr=popen(ps, "r"))!=NULL)   
    {   
        while(fgets(buf_ps, 1024, ptr)!=NULL)   
        {   
           strcat(result, buf_ps);   
           if(strlen(result)>1024)   
               break;   
        }   
        pclose(ptr);   
        ptr = NULL;   
    }   
    else  
    {   
        printf("popen %s error\n", ps);   
    }   
}

void MemoryUtil::PrintStack()
{
    int size = 16;
    void * array[16];
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (int i = 0; i < stack_num; ++i)
    {
        printf("%s\n", stacktrace[i]);
    }
    free(stacktrace);
}

void MemoryUtil::PrintStack(const char* pFileName)
{
        const int maxLevel = 32;
    void* buffer[maxLevel];
    int level = backtrace(buffer, maxLevel);
    char ** stacktrace = backtrace_symbols(buffer, maxLevel);
    //printf("level: %d\n", level);
    for (int i = 0; i < level; ++i)
    {
        char result[256] = {0};
        char addrCmd[256] = { 0 };
        sprintf(addrCmd, "addr2line -e %s %p", pFileName, buffer[i]);
        MemoryUtil::executeCMD(addrCmd, result);
        printf("%s\t%s", stacktrace[i], result);
    }
    free(stacktrace);
}

} // namespace tb


#endif
